參考資料:
快速發行給分離式前端使用的 Token
使用指令安裝:
composer require laravel/sanctum
建立設置:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
執行 migrate:
貌似 Laravel 9 在建立專案時已經包含這個 migration ,因此沒執行是正常的
php artisan migrate
編輯 App/Http/Kernel.php 將 Middleware 添加至 api 中:
Laravel 9 預設就已經有了,將註解拿掉就好
'api' => [
    \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
    'throttle:api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],
則需要修改 config/sanctum.php,並在 guard 後面補上你在 config/auth.php 中新增的 guard:
'guard' => ['web', 'store'],
先編輯要使用 token 的 Model,加入 HasApiTokens:
use Laravel\Sanctum\HasApiTokens;
class Store extends Authenticatable
{
    use HasApiTokens, HasFactory;
	...
之後就能使用 createToken 方法去產生 token:
$user->createToken('token-name')->plainTextToken;
使用 AuthController 來實作 API 登入,並取得 token:
use Validator;
use Auth;
class AuthController extends Controller
{
    public function storeLogin (Request $request)
    {
        $validator = Validator::make($request->all(), [
            'username' => 'required',
            'password' => 'required|min:6',
        ]);
        if ($validator->fails()) return response()->json(['message' => $validator->errors()->first()], 400);
        if (Auth::guard('store')->attempt(['username' => $request->username, 'password' => $request->password])) {
            $store = Auth::guard('store')->user();
            return $store->createToken('jwt')->plainTextToken;
        }
        return response()->json(['message' => 'username or password incorrect'], 400);
    }
}
編輯 routes/api.php,一樣將要受 token 保護的路由放入 middleware :
// 登入
Route::post('/store-login', 'App\Http\Controllers\AuthController@storeLogin');
Route::middleware(['auth:sanctum'])->group(function () {
    Route::get('/posts', function () {
        return '200';
    });
});
如果 ID 不是整數,則需要修改資料表 personal_access_tokens 將 tokenable_id 改為 String
public function up()
    {
        Schema::table('personal_access_tokens', function (Blueprint $table) {
            $table->string('tokenable_id')->change();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('personal_access_tokens', function (Blueprint $table) {
            $table->unsignedBigInteger('tokenable_id')->change();
        });
    }